#include <linux/init.h>
#include <linux/module.h>
#include <linux/of_irq.h>
#include <linux/of.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/sched.h>
#include <linux/poll.h>

#include <asm/io.h>
#include <asm/uaccess.h>

#define GPXDATA_REG 0x11000c20

#define KEY_ENTER 28

struct key_user{
	int type;
	int value;// 0 1
};


//描述按键信息
struct key_desc{
	unsigned int dev_major;
	struct class *cls;
	struct device *dev;
	int irqnum;
	void *virbase;
	struct key_user event;
	wait_queue_head_t wq_head;
	int key_state;
};

struct key_desc *key_dev;

irqreturn_t key_irq_handler(int irqnum,void *devid)
{
	int value;
	printk("---------key3-------\n");
	value = readl(key_dev->virbase+4)&(1<<2);
	if(value)
	{
		printk("key3 up\n");
		key_dev->event.type = KEY_ENTER;
		key_dev->event.value = 0;
	}
	else
	{
		printk("key3 press\n");
		key_dev->event.type = KEY_ENTER;
		key_dev->event.value = 1;
	}

	//表示有数据
	wake_up_interruptible(&key_dev->wq_head);
	//设置标志位
	key_dev->key_state=1;

	return IRQ_HANDLED;
}

int get_irqno_from_node(void)
{
	int irqnum;
	//名字为节点
	struct device_node *np = of_find_node_by_path("/key_init_node");
	if(np)
	{
		printk("find node ok\n");
	}
	else
	{
		printk("find node failed\n");
	}

	irqnum=irq_of_parse_and_map(np,0);
	printk("irqnum=%d\n",irqnum);
	return irqnum;
}


ssize_t chr_drv_open(struct inode *inode,struct file *filp)
{
	printk("----%s----\n",__FUNCTION__);
	return 0;
}
ssize_t chr_drv_read(struct file *filp,char __user *buf,size_t count,loff_t *fpos)
{
	int ret;
	
	//非阻塞并且没数据，返回出错码
	if(filp->f_flags&O_NONBLOCK&&!key_dev->key_state)
		return -EAGAIN;

	//没有数据进行休眠
	wait_event_interruptible(key_dev->wq_head,key_dev->key_state);


	ret=raw_copy_to_user(buf,&key_dev->event,count);
	if(ret>0)
	{
		printk("copy_to_user failed\n");
		return -EFAULT;
	}

	memset(&key_dev->event,0,sizeof(key_dev->event));
	key_dev->key_state=0;
	return count;
}
ssize_t chr_drv_write(struct file *filp,const char __user *buf,size_t count,loff_t *fpos)
{
/*	int ret;
	int value;

	ret=raw_copy_from_user(&value,buf,count);
	if(ret>0)
	{
		printk("copy_to_user failed\n");
		return -EFAULT;
	}
	//对灯进行控制
	if(value)
	{
	//	*gpx2data |= (1<<0);
		writel(readl(led_dev->vir_base+4)|(1<<0),led_dev->vir_base+4);
	}
	else
	{
	//	*gpx2data &= ~(1<<0);
		writel(readl(led_dev->vir_base+4)&~(1<<0),led_dev->vir_base+4);
	}
*/
	return 0;
}
ssize_t chr_drv_close(struct inode *inode,struct file *filp)
{
	printk("----%s----\n",__FUNCTION__);
	return 0;
}

unsigned int key_dev_poll(struct file *filp,struct poll_table_struct *pts)
{
	unsigned int mask;
	poll_wait(filp,&key_dev->wq_head,pts);

	if(!key_dev->key_state)
	{
		mask=0;
	}
	if(key_dev->key_state)
	{
		mask|=POLLIN;
	}
	return mask;
}

const struct file_operations key_fops = {
	.open = chr_drv_open,//对open赋值
	.read = chr_drv_read,
	.write = chr_drv_write,
	.release = chr_drv_close,
	.poll = key_dev_poll,
};

static int __init key_drv_init(void)
{
	int ret;
	//四部曲：
	//设定一个全局设备对象
	key_dev=kzalloc(sizeof(struct key_desc),GFP_KERNEL);
	//申请主设备号
	key_dev->dev_major=register_chrdev(0,"key_dev",&key_fops);
	//创建设备节点文件
	key_dev->cls=class_create(THIS_MODULE,"key_cls");
	key_dev->dev=device_create(key_dev->cls,NULL,MKDEV(key_dev->dev_major,0),NULL,"key_lucky");
	//获取中断
	key_dev->irqnum=get_irqno_from_node();
	//申请中断，key3为中断名
	ret = request_irq(key_dev->irqnum,key_irq_handler,IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,"key3",NULL);
	if(ret != 0)
	{
		printk("request_irq failed\n");
		return ret;
	}
	//硬件的初始化
	key_dev->virbase=ioremap(GPXDATA_REG,8);
	//初始化等待队列头
	init_waitqueue_head(&key_dev->wq_head);
	return 0;
}

static void __exit key_drv_exit(void)
{
	iounmap(key_dev->virbase);
 	free_irq(key_dev->irqnum,NULL);
	device_destroy(key_dev->cls,MKDEV(key_dev->dev_major,0));
	class_destroy(key_dev->cls);
	unregister_chrdev(key_dev->dev_major,"key_dev");
	kfree(key_dev);
}

module_init(key_drv_init);
module_exit(key_drv_exit);
MODULE_LICENSE("GPL");

